package com.example.base_model.mvvm

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import com.example.base_model.bean.BaseHttpBean
import com.example.base_model.bean.isSuccess
import com.example.base_model.network.error.ExceptionMapper
import com.example.base_model.network.load.LoadingStrategy
import com.example.base_model.network.load.LoadingUiState
import com.example.base_model.network.request_param.RequestParamInterface
import com.example.base_model.network.result.BaseResult
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

/**
 * @Author jiangKunKun
 * @CreateDate 2025/6/23
 * @Description BaseViewModel
 * @SpittingGrooves
 */

open class BaseViewModel : ViewModel(){
    /**
     *
     * 一个功能强大的协程启动器，专为遵循MVI架构模式的ViewModel设计。
     * 它封装了网络请求的完整生命周期管理（开始、成功/失败、错误、结束），
     * 并能自动处理与UI相关的加载状态（例如显示/隐藏加载对话框或动画）。
     *
     * 此版本适用于那些需要精细管理加载状态的场景，通过`LoadingUiState`接口和`LoadingStrategy`实现。
     *
     * @param T 网络API直接返回的数据实体类型（通常在 `BaseHttpBean<T>` 中）。
     * @param R 经过`transform`闭包转换后，最终更新到UI状态中的数据类型。
     * @param U ViewModel的UI状态类型，必须实现 `LoadingUiState` 接口以支持加载状态管理。
     *
     * @param stateFlow 管理UI状态的 `MutableStateFlow` 实例。函数将通过此Flow发射状态更新。
     * @param loadingStrategy 加载策略枚举，用于区分不同类型的加载（如全屏加载、下拉刷新、加载更多）。
     *                      `NONE`表示不处理加载状态。默认为`NONE`。
     * @param stateUpdateBlock 一个高阶函数，定义了如何将新的网络请求结果 `BaseResult<R>` 合并到当前的UI状态 `U` 中。
     *                         这是实现MVI单向数据流中 "Reduce" 步骤的核心。
     *                         示例：`{ currentState, result -> currentState.copy(userResult = result) }`
     * @param block suspend闭包，在此处执行实际的网络请求调用。它应该返回一个 `BaseHttpBean<T>` 类型的对象。
     *              示例：`{ apiService.getUser("123") }`
     * @param transform (可选) 一个转换函数，用于在网络请求成功后、更新UI状态之前对数据进行预处理或类型转换。
     *                  它接收原始数据 `T?`，并返回UI状态所需的数据 `R?`。默认为一个简单的类型转换。
     *                  示例：`{ userDto -> userDto?.toUserVo() }`
     * @param requestParam (可选) 请求参数对象，实现了`RequestParamInterface`接口。当请求失败或需要重试时，
     *                     可以将原始参数随失败状态一同传递给UI层，便于调试或实现重试逻辑。
     * @param onStart (可选) 操作开始时的回调函数。
     * @param onDataFail (可选) 操作业务错误的回调函数。
     * @param onError (可选) 发生异常时的回调函数。
     * @param onFinish (可选) 操作完成时的回调函数。
     *
     * @return 返回一个 `Job` 对象，可以用于取消这个协程任务。
     */
    inline fun <T, R, U : LoadingUiState> launchWithStateFlow(
        stateFlow: MutableStateFlow<U>,
        loadingStrategy: LoadingStrategy = LoadingStrategy.NONE,
        crossinline stateUpdateBlock: (currentState: U, result: BaseResult<R>) -> U,
        noinline block: suspend () -> BaseHttpBean<T>,
        crossinline transform: (T?) -> R? = { it as? R },
        requestParam: RequestParamInterface? = null,
        crossinline onStart: (() -> Unit) = {},
        crossinline onDataFail: ((Int, String, RequestParamInterface?) -> Unit) = { code, msg, param -> },
        crossinline onError: ((Exception) -> Unit) = {},
        crossinline onFinish: (() -> Unit) = { }
    ): Job {
        return launchExceptionHandler {
            // 1. 【开始阶段】- 管理加载状态
            // 如果指定了加载策略（非NONE），则增加对应策略的加载计数。
            // StateFlow的update是原子操作，保证了并发安全。
            if (loadingStrategy != LoadingStrategy.NONE) {
                stateFlow.update { currentState ->
                    val currentGroups = currentState.loadingGroups
                    val newCount = (currentGroups[loadingStrategy] ?: 0) + 1
                    // 调用LoadingUiState的copyWithLoading接口来创建一个新的、包含更新后加载状态的UiState实例。
                    @Suppress("UNCHECKED_CAST")
                    currentState.copyWithLoading(currentGroups + (loadingStrategy to newCount)) as U
                }
            }

            // 2. 【状态更新：开始】
            // 发射一个 `start` 状态，通知UI网络请求已启动。
            onStart.invoke()
            stateFlow.update { stateUpdateBlock(it, BaseResult.start()) }

            try {
                // 3. 【执行网络请求】
                // 在协程中挂起并执行传入的 `block` 函数，即实际的API调用。
                val result = block()

                // 4. 【状态更新：成功/业务失败】
                // 根据API返回的业务码判断是成功还是业务逻辑失败。
                val finalResult: BaseResult<R> = if (result.isSuccess()) {
                    // 业务成功：调用 `transform` 函数处理数据，然后包装成 `dataSuccess` 状态。
                    BaseResult.dataSuccess(transform(result.data), requestParam)
                } else {
                    // 业务失败：包装成 `dataFail` 状态，包含错误码和错误信息。
                    onDataFail.invoke(result.code, result.msg, requestParam)
                    BaseResult.dataFail(result.code, result.msg, transform(result.data), requestParam)
                }
                // 将成功或业务失败的结果发射出去。
                stateFlow.update { stateUpdateBlock(it, finalResult) }

            } catch (e: Exception) {
                // 5. 【状态更新：异常】
                // 如果在网络请求或数据处理过程中发生异常（如网络中断、JSON解析错误等）。
                LogUtils.d("异常", e.message, e.toString())
                onError.invoke(e)
                // 将通用异常映射为定义的ApiError。
                val error = ExceptionMapper.mapToApiError(e)
                // 发射一个 `error` 状态，包含错误信息和异常对象。
                stateFlow.update { stateUpdateBlock(it, BaseResult.error(error.code, error.msg, e, requestParam)) }
            } finally {
                // 6. 【结束阶段】- 管理加载状态
                // 无论请求成功、失败还是异常，都会执行。
                // 如果指定了加载策略，则减少对应策略的加载计数。
                if (loadingStrategy != LoadingStrategy.NONE) {
                    stateFlow.update { currentState ->
                        val currentGroups = currentState.loadingGroups
                        // 保证计数器不会小于0。
                        val newCount = ((currentGroups[loadingStrategy] ?: 0) - 1).coerceAtLeast(0)
                        @Suppress("UNCHECKED_CAST")
                        currentState.copyWithLoading(currentGroups + (loadingStrategy to newCount)) as U
                    }
                }
                // 7. 【状态更新：结束】
                // 发射一个 `finish` 状态，通知UI整个请求流程已结束，可以用于隐藏一些非阻塞性的加载提示。
                onFinish.invoke()
                stateFlow.update { stateUpdateBlock(it, BaseResult.finish()) }
            }
        }
    }


    /**
     *
     * 一个简化的协程启动器，同样用于MVI架构。与完整版不同，此版本不处理任何加载状态 (`LoadingStrategy`)。
     * 它的核心职责是统一封装网络请求的生命周期（start, success/fail, error, finish），
     * 适用于不希望与 `LoadingUiState` 接口耦合或不需要自动加载状态管理的场景。
     *
     * @param T 网络API直接返回的数据实体类型（在 `BaseHttpBean<T>` 中）。
     * @param R 经过`transform`闭包转换后，最终更新到UI状态中的数据类型。
     * @param U ViewModel的UI状态类型。可以是任何类型（data class, sealed class, etc.）。
     *
     * @param stateFlow 管理UI状态的 `MutableStateFlow` 实例。
     * @param stateUpdateBlock 定义如何将网络请求结果 `BaseResult<R>` 合并到当前UI状态 `U` 中的高阶函数。
     * @param block suspend闭包，执行实际的网络请求调用，返回 `BaseHttpBean<T>`。
     * @param transform (可选) 数据转换函数，与完整版功能相同。
     * @param requestParam (可选) 请求参数对象，与完整版功能相同。
     * @param onStart (可选) 操作开始时的回调函数。
     * @param onDataFail (可选) 操作业务错误的回调函数。
     * @param onError (可选) 发生异常时的回调函数。
     * @param onFinish (可选) 操作完成时的回调函数。
     *
     * @return 返回一个 `Job` 对象。
     */
    inline fun <T, R, U> launchWithStateFlow(
        stateFlow: MutableStateFlow<U>,
        crossinline stateUpdateBlock: (currentState: U, result: BaseResult<R>) -> U,
        noinline block: suspend () -> BaseHttpBean<T>,
        crossinline transform: (T?) -> R? = { it as? R },
        requestParam: RequestParamInterface? = null,
        crossinline onStart: (() -> Unit) = {},
        crossinline onDataFail: ((Int, String, RequestParamInterface?) -> Unit) = { code, msg, param -> },
        crossinline onError: ((Exception) -> Unit) = {},
        crossinline onFinish: (() -> Unit) = { }
    ): Job {
        return launchExceptionHandler {
            // 1. 【状态更新：开始】
            // 发射 `start` 状态，通知UI请求已开始。
            onStart.invoke()
            stateFlow.update { stateUpdateBlock(it, BaseResult.start()) }

            try {
                // 2. 【执行网络请求】
                val result = block()

                // 3. 【状态更新：成功/业务失败】
                val finalResult: BaseResult<R> = if (result.isSuccess()) {
                    // 业务成功，转换数据后包装成 `dataSuccess` 状态。
                    BaseResult.dataSuccess(transform(result.data), requestParam)
                } else {
                    // 业务失败，包装成 `dataFail` 状态。
                    onDataFail.invoke(result.code, result.msg, requestParam)
                    BaseResult.dataFail(result.code, result.msg, transform(result.data), requestParam)
                }
                stateFlow.update { stateUpdateBlock(it, finalResult) }

            } catch (e: Exception) {
                // 4. 【状态更新：异常】
                LogUtils.d("异常", e.message, e.toString())
                onError.invoke(e)
                val error = ExceptionMapper.mapToApiError(e)
                // 发生异常，发射 `error` 状态。
                stateFlow.update { stateUpdateBlock(it, BaseResult.error(error.code, error.msg, e, requestParam)) }
            } finally {
                // 5. 【状态更新：结束】
                // 无论结果如何，最后都发射 `finish` 状态。
                onFinish.invoke()
                stateFlow.update { stateUpdateBlock(it, BaseResult.finish()) }
            }
        }
    }

    /**
     * 专为本地数据源（Room, DataStore）设计，API与网络请求版本完全对齐。
     * 封装了操作的生命周期，并能管理加载状态。
     *
     * @param T 本地操作直接返回的数据类型 (e.g., User, List<User>, Long)。
     * @param R 经过`transform`转换后，在UI状态中呈现的数据类型。
     * @param U ViewModel的UI状态类型，必须实现`LoadingUiState`。
     *
     * @param stateFlow 管理UI状态的 `MutableStateFlow` 实例。
     * @param loadingStrategy 加载策略枚举，用于区分不同类型的加载（如全屏加载、下拉刷新、加载更多）。
     *                      `NONE`表示不处理加载状态。默认为`NONE`。
     * @param stateUpdateBlock 定义如何将操作结果 `BaseResult<R>` 合并到当前UI状态 `U` 中的高阶函数。
     * @param block suspend闭包，执行实际的本地操作。
     * @param transform (可选) 数据转换函数，与完整版功能相同。
     * @param requestParam (可选) 请求参数对象，与完整版功能相同。
     * @param onStart (可选) 操作开始时的回调函数。
     * @param onError (可选) 发生异常时的回调函数。
     * @param onFinish (可选) 操作完成时的回调函数。
     *
     * @return 返回一个 `Job` 对象。
     */
    inline fun <T, R, U : LoadingUiState> launchLocWithStateFlow(
        stateFlow: MutableStateFlow<U>,
        loadingStrategy: LoadingStrategy = LoadingStrategy.NONE,
        crossinline stateUpdateBlock: (currentState: U, result: BaseResult<R>) -> U,
        noinline block: suspend () -> T,
        crossinline transform: (T) -> R = { it as R },
        requestParam: RequestParamInterface? = null,
        crossinline onStart: (() -> Unit) = {},
        crossinline onError: ((Exception) -> Unit) = {},
        crossinline onFinish: (() -> Unit) = { }
    ): Job {
        return launchExceptionHandler {
            // 1. 【开始阶段】- 管理加载状态
            if (loadingStrategy != LoadingStrategy.NONE) {
                stateFlow.update { currentState ->
                    val currentGroups = currentState.loadingGroups
                    val newCount = (currentGroups[loadingStrategy] ?: 0) + 1
                    @Suppress("UNCHECKED_CAST")
                    currentState.copyWithLoading(currentGroups + (loadingStrategy to newCount)) as U
                }
            }
            // 2. 【状态更新：开始】
            onStart.invoke()
            stateFlow.update { stateUpdateBlock(it, BaseResult.start()) }
            try {
                // 3. 【执行本地操作】
                val result = block()
                // 4. 【状态更新：成功】
                // 本地操作只要不抛出异常即视为成功。直接调用transform并包装为dataSuccess。
                val successResult: BaseResult<R> = BaseResult.dataSuccess(transform(result), requestParam)
                stateFlow.update { stateUpdateBlock(it, successResult) }
            } catch (e: Exception) {
                // 5. 【状态更新：异常】
                LogUtils.d("本地操作异常", e.message, e.toString())
                onError.invoke(e)
                val error = ExceptionMapper.mapToApiError(e)
                stateFlow.update { stateUpdateBlock(it, BaseResult.error(error.code, error.msg, e, requestParam)) }
            } finally {
                // 6. 【结束阶段】- 管理加载状态
                if (loadingStrategy != LoadingStrategy.NONE) {
                    stateFlow.update { currentState ->
                        val currentGroups = currentState.loadingGroups
                        val newCount = ((currentGroups[loadingStrategy] ?: 0) - 1).coerceAtLeast(0)
                        @Suppress("UNCHECKED_CAST")
                        currentState.copyWithLoading(currentGroups + (loadingStrategy to newCount)) as U
                    }
                }
                // 7. 【状态更新：结束】
                onFinish.invoke()
                stateFlow.update { stateUpdateBlock(it, BaseResult.finish()) }
            }
        }
    }

    /**
     * 不处理加载状态 ([LoadingStrategy]) 的简化版本。
     *
     * @param T 本地操作直接返回的数据类型 (e.g., User, List<User>, Long)。
     * @param R 经过`transform`转换后，在UI状态中呈现的数据类型。
     * @param U ViewModel的UI状态类型。可以是任何类型（data class, sealed class, etc.）。
     *
     * @param stateFlow 管理UI状态的 `MutableStateFlow` 实例。
     * @param stateUpdateBlock 定义如何将操作结果 `BaseResult<R>` 合并到当前UI状态 `U` 中的高阶函数。
     * @param block suspend闭包，执行实际的本地操作。
     * @param transform (可选) 数据转换函数，与完整版功能相同。
     * @param requestParam (可选) 请求参数对象，与完整版功能相同。
     * @param onStart (可选) 操作开始时的回调函数。
     * @param onError (可选) 发生异常时的回调函数。
     * @param onFinish (可选) 操作完成时的回调函数。
     *
     * @return 返回一个 `Job` 对象。
     */
    inline fun <T, R, U> launchLocWithStateFlow(
        stateFlow: MutableStateFlow<U>,
        crossinline stateUpdateBlock: (currentState: U, result: BaseResult<R>) -> U,
        noinline block: suspend () -> T,
        crossinline transform: (T) -> R = { it as R },
        requestParam: RequestParamInterface? = null,
        crossinline onStart: (() -> Unit) = {},
        crossinline onError: ((Exception) -> Unit) = {},
        crossinline onFinish: (() -> Unit) = { }
    ): Job {
        return launchExceptionHandler {
            onStart.invoke()
            stateFlow.update { stateUpdateBlock(it, BaseResult.start()) }
            try {
                val result = block()
                val successResult: BaseResult<R> = BaseResult.dataSuccess(transform(result), requestParam)
                stateFlow.update { stateUpdateBlock(it, successResult) }
            } catch (e: Exception) {
                LogUtils.d("本地操作异常", e.message, e.toString())
                onError.invoke(e)
                val error = ExceptionMapper.mapToApiError(e)
                stateFlow.update { stateUpdateBlock(it, BaseResult.error(error.code, error.msg, e, requestParam)) }
            } finally {
                onFinish.invoke()
                stateFlow.update { stateUpdateBlock(it, BaseResult.finish()) }
            }
        }
    }

    /**
     * 创建一个带有异常处理器的协程作用域
     */
    private val exceptionHandler = CoroutineExceptionHandler { context, throwable ->
        LogUtils.e("CoroutineException", "Uncaught exception in coroutine: ${throwable.message}", throwable)
        // 可以在这里添加统一的异常处理逻辑，如上报异常等
    }

    /**
     * 创建一个带有异常处理器的协程作用域
     */
    fun launchExceptionHandler(block: suspend CoroutineScope.() -> Unit): Job {
        return viewModelScope.launch(exceptionHandler, block = block)
    }

}